package me.chenqiang.cinc.passportutils;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import me.chenqiang.cinc.CincCodingException;
import me.chenqiang.cinc.codes.ISO3166Utils;
import me.chenqiang.cinc.codes.ISO3166Utils.Record;

public class CountryHuffmanCodec {
	private static final Logger LOGGER = LoggerFactory.getLogger(CountryHuffmanCodec.class);
	public static final Map<String, String> CODEC_MAP;
	protected static final StringPrefixFreeCodecTrie<String> DECODING_TREE;
	protected static final Exception LAST_ERROR;
	
	private CountryHuffmanCodec() {}
	
	static {
		Map<String, String> codecMap = new HashMap<>(256);
		StringPrefixFreeCodecTrie<String> tree = new StringPrefixFreeCodecTrie<>();
		Exception lastError = null;
		
		try(InputStream jsonFile = CountryHuffmanCodec.class.getResourceAsStream("country_codec.json")) {
			ObjectMapper mapper = new ObjectMapper();
			ObjectNode dict = (ObjectNode)mapper.readTree(new InputStreamReader(jsonFile, StandardCharsets.UTF_8));
			for(Entry<String, JsonNode> entry: dict.properties()) {
				codecMap.put(entry.getKey(), entry.getValue().asText());
			}

			for(Entry<String, String> entry: codecMap.entrySet()) {
				tree.insert(entry.getValue(), entry.getKey());
			}
			
			LOGGER.debug("文件country_codec.json加载完成{}项", dict.size());			
		} catch (IOException e) {
			lastError = e;
			LOGGER.error("文件country_codec.json文件加载或解析失败", e);			
		} finally {
			CODEC_MAP = Collections.unmodifiableMap(codecMap);
			DECODING_TREE = tree;
			LAST_ERROR = lastError;
		}
	}
	
	public static String encode(String countryCode) throws CincCodingException {
		Record rec = ISO3166Utils.getCountry(countryCode);
		if(CODEC_MAP.containsKey(rec.getAlpha2())) {
			return CODEC_MAP.get(rec.getAlpha2());
		}
		else {
			throw new CincCodingException(String.format("国别代码%s不属于支持范围内", countryCode));
		}
	}
	
	public static List<String> decode(String encoded, int limit) throws CincCodingException {
		ArrayList<String> res = new ArrayList<>();
		try {
			for(Entry<String, String> val: DECODING_TREE.items(encoded)) {
				if(LOGGER.isDebugEnabled()) LOGGER.debug(String.format("%s -> %s", val.getKey(), val.getValue()));
				res.add(val.getValue());
				if(limit > 0 && res.size() == limit) {
					return res;
				}
			}
		}
		catch(IllegalArgumentException iae) {
			throw new CincCodingException(iae);
		}
		return res;
	}
	
}
